﻿using System;
using System.Collections;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using Apewer.Internals;
using Apewer;

namespace Apewer.Network
{

    /// <summary>TCP 服务端。</summary>
    internal class TcpServer
    {

        #region event

        /// <summary>Exception。</summary>
        public Event<Exception> Excepted { get; set; }

        /// <summary>服务端已启动。</summary>
        public Event<SocketEndPoint> Started { get; set; }

        /// <summary>服务端已关闭。</summary>
        public Event<SocketEndPoint> Quitted { get; set; }

        /// <summary>客户端已连接。</summary>
        public Event<SocketEndPoint> Connected { get; set; }

        /// <summary>客户端已断开。</summary>
        public Event<SocketEndPoint> Closed { get; set; }

        /// <summary>已收到客户端数据。</summary>
        public Event<SocketReceived> Received { get; set; }

        #region raise

        internal void RaiseExcepted(Exception exception)
        {
            if (Excepted != null) Excepted(this, exception);
        }

        internal void RaiseStarted()
        {
            if (Started != null)
            {
                var ip = _endpoint == null ? "" : _endpoint.Address.ToString();
                var port = _endpoint == null ? 0 : _endpoint.Port;
                Started?.Invoke(this, new SocketEndPoint(ip, port));
            }
        }

        internal void RaiseConnected(string ip, int port)
        {
            Connected?.Invoke(this, new SocketEndPoint(ip, port));
        }

        internal void RaiseClosed(string ip, int port)
        {
            Closed?.Invoke(this, new SocketEndPoint(ip, port));
        }

        internal void RaiseQuitted()
        {
            var quitted = Quitted;
            if (quitted != null)
            {
                var ip = _endpoint == null ? "" : _endpoint.Address.ToString();
                var port = _endpoint == null ? 0 : _endpoint.Port;
                quitted(this, new SocketEndPoint(ip, port));
            }
        }

        internal void RaiseReceived(string ip, int port, byte[] bytes)
        {
            Received?.Invoke(this, new SocketReceived(ip, port, bytes));
        }

        #endregion

        #endregion

        #region definition

        private Socket _socket = null;
        private Thread _listener = null;
        private SortedList _client = null;
        private IPEndPoint _endpoint = null;

        private int _port = 0;
        private int _max = 0;
        private int _timeout = 0;
        private bool _state = false;
        private bool _background = true;

        /// <summary>构造函数。</summary>
        public TcpServer(int port = 0)
        {
            Port = port;
            Max = 1000;
            Timeout = 1000;
        }

        #endregion

        #region accessor

        /// <summary>获取或设置监听线程是否为后台线程，默认为“是”。</summary>
        public bool Background
        {
            get { return _background; }
            set
            {
                _background = value;
                try { if (_listener != null) _listener.IsBackground = value; }
                catch (Exception ex) { RaiseExcepted(ex); }
            }
        }

        /// <summary>端口。</summary>
        public int Port
        {
            get
            {
                int port = _port;
                if (port > 65535) port = 65535;
                if (port < 0) port = 0;
                return port;
            }
            set
            {
                int port = value;
                if (port < 0) port = 0;
                if (port > 65535) port = 65535;
                _port = port;
            }
        }

        /// <summary>最大客户端数量。</summary>
        public int Max
        {
            get { return _max; }
            set { _max = (value > 0) ? value : 0; }
        }

        /// <summary>发送数据和接收数据的超时时间。</summary>
        public int Timeout
        {
            get { return _timeout; }
            set { _timeout = (value > 0) ? value : 0; }
        }

        /// <summary>服务端正在运行。</summary>
        public bool Alive
        {
            get { return (_listener != null) ? _listener.IsAlive : false; }
        }

        /// <summary>已连接的客户端数量。</summary>
        public int Count
        {
            get { return (_client != null) ? _client.Count : 0; }
        }

        /// <summary>启动服务端。</summary>
        public bool Start(bool inBackground = true)
        {
            try
            {
                _endpoint = new IPEndPoint(IPAddress.Any, Port);
                _client = new SortedList();
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.Bind(_endpoint);
                _socket.Listen(Max);
                _socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.AcceptConnection, 1);
                _socket.ReceiveTimeout = Timeout;
                _socket.SendTimeout = Timeout;

                if (inBackground)
                {
                    _listener = new Thread(Listener);
                    _listener.IsBackground = Background;
                    _listener.Start();
                    _state = _listener.IsAlive;
                    var cep = (IPEndPoint)_socket.LocalEndPoint;
                    _port = cep.Port;
                    RaiseStarted();
                    return true;
                }
                else
                {
                    _state = true;
                    var cep = (IPEndPoint)_socket.LocalEndPoint;
                    _port = cep.Port;
                    RaiseStarted();
                    Listener();
                    return true;
                }
            }
            catch (Exception ex)
            {
                _endpoint = null;
                RaiseExcepted(ex);
                if ((_socket != null) && _socket.Connected) _socket.Close();
                _state = false;
                Quit();
                return false;
            }
        }

        /// <summary>关闭服务端。</summary>
        public void Quit()
        {
            Quit(true);
        }

        /// <summary>断开与所有客户端的连接。</summary>
        public void Close()
        {
            if (_client != null)
            {
                foreach (Socket socket in _client.Values)
                {
                    try
                    {
                        var ep = (IPEndPoint)socket.RemoteEndPoint;
                        Close(ep.Address.ToString(), ep.Port);
                    }
                    catch (Exception ex)
                    {
                        RaiseExcepted(ex);
                    }
                }
            }
        }

        /// <summary>断开与指定客户端的连接。</summary>
        public void Close(string ip, int port)
        {
            if (port < 0) port = 0;
            if (port > 65535) port = 65535;

            if (!string.IsNullOrEmpty(ip))
            {
                var ck = ip + "-" + port.ToString();
                var cs = Client(ip, port);
                try
                {
                    if (cs != null) cs.Close();
                    Remove(ck);
                    RaiseClosed(ip, port);
                }
                catch (Exception ex)
                {
                    RaiseExcepted(ex);
                }
            }
        }

        /// <summary>向所有客户端广播数据。</summary>
        public void Send(byte[] bytes)
        {
            var length = bytes.Length;
            if ((_client.Count > 0) && (length > 0))
            {
                foreach (Socket i in _client.Values) Send(bytes, i);
            }
        }

        /// <summary>向指定客户端发送数据。</summary>
        public bool Send(byte[] bytes, string ip, int port)
        {
            if (port < 0) port = 0;
            if (port > 65535) port = 65535;

            if (string.IsNullOrEmpty(ip)) return false;
            return Send(bytes, Client(ip, port));
        }

        #endregion

        #region logic

        /// <summary>
        /// 
        /// </summary>
        /// <param name="event"></param>
        private void Quit(bool @event)
        {
            Close();
            if (_listener != null)
            {
                if (_listener.IsAlive) _listener.Abort();
                _listener = null;
            }
            if (_socket != null)
            {
                _socket.Close();
                _socket = null;
            }
            if (_client != null)
            {
                _client.Clear();
                _client = null;
            }
            _endpoint = null;
            if (@event) RaiseQuitted();
        }

        private void Close(Socket socket)
        {
            if (socket != null)
            {
                try { socket.Close(); }
                catch (Exception ex) { RaiseExcepted(ex); }
            }
        }

        internal void Remove(string key)
        {
            if ((_client != null) && (!string.IsNullOrEmpty(key)))
            {
                if (_client.ContainsKey(key))
                {
                    try
                    {
                        Close((Socket)_client[key]);
                        _client.Remove(key);
                    }
                    catch (Exception ex)
                    {
                        RaiseExcepted(ex);
                    }
                }
            }
        }

        private bool Send(byte[] bytes, Socket client)
        {
            var length = bytes.Length;
            if ((client != null) && (length > 0))
            {
                try
                {
                    return (client.Send(bytes, length, SocketFlags.None) > 0) ? true : false;
                }
                catch (Exception ex) { RaiseExcepted(ex); }
            }
            return false;
        }

        private void Listener()
        {
            while (_socket != null)
            {
                try
                {
                    var socket = _socket.Accept();
                    if (socket != null)
                    {
                        var ep = (IPEndPoint)socket.RemoteEndPoint;
                        var key = ep.Address.ToString() + "-" + ep.Port.ToString();
                        _client.Add(key, socket);
                        var instance = new TcpInstance(this, _socket, socket);
                        var thread = new Thread(instance.Process);
                        thread.IsBackground = true;
                        thread.Name = key;
                        thread.Start();
                        RaiseConnected(ep.Address.ToString(), ep.Port);
                    }
                }
                catch (Exception ex)
                {
                    RaiseExcepted(ex);
                }
            }
            Quit(false);
        }

        private Socket Client(string ip, int port)
        {
            if (port < 0) port = 0;
            if (port > 65535) port = 65535;
            try
            {
                var ck = ip + "-" + port.ToString();
                if (_client.ContainsKey(ck)) return (Socket)_client[ck];
            }
            catch (Exception ex)
            {
                RaiseExcepted(ex);
            }
            return null;
        }

        #endregion

    }
}
